// File CombatInterface.cpp
//
// Purpose: Provides functions for combat operations
//
// Author : Bryson King
// Date : 23 March 2013
// --------------------------------------------------------------
#include "CombatInterface.h"

#include <iostream>
#include <iomanip>
#include <string>
#include <algorithm>	// complex string operations

#include "misc/ConsoleUtils.h"
#include "IPerson.h"
#include "Item.h"
#include "Random.h"
#include "UserInterface.h"

using namespace std;

const int DAMAGE_DONE_BY_FISTS = 2;
const int PERCENT_CHANCE_FOR_PERSON_TO_HIT_WITH_RANGED_WEAPON = 30;
const int PERCENT_CHANCE_FOR_PERSON_TO_HIT_WITH_MELEE_WEAPON = 50;
const int PERCENT_CHANCE_FOR_ENEMY_TO_HIT_WITH_RANGED_WEAPON = 20;
const int PERCENT_CHANCE_FOR_ENEMY_TO_HIT_WITH_MELEE_WEAPON = 60;

CCombatInterface::CCombatInterface() : m_bCombatComplete(true), m_cEnemy(nullptr),
	m_cPerson(nullptr), m_sWeaponUsedByEnemy(nullptr), m_sWeaponUsedByPlayer(nullptr)
{}

CCombatInterface::~CCombatInterface()
{}

void CCombatInterface::StartCombat(IPerson* &pPlayer, IPerson* &pEnemy)
{
}

void CCombatInterface::StartCombatWithPlayer(IPerson* &pPlayer, IPerson* &pEnemy)
{
	string sUserInput;

	// First, set values of member variables
	m_bCombatComplete = false;
	m_cEnemy = pEnemy;
	m_cPerson = pPlayer;

	// Retrieve items available to user
	int totalInArray;
	SItem** arrayOfItems = pPlayer->RetrieveAllWeaponsFromInventory(totalInArray);

	SetTextColor(BACKGROUND_RED|FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN|FOREGROUND_INTENSITY);

	// Check for weapon options
	if (totalInArray < 1)
	{
		// Nothing available in player's inventory
		// Player must use his/her fists as their only option
	} else {
		// Output to user that combat is starting and who combat is starting with
		cout << endl << endl;
		cout << "You have chosen to attack " << pEnemy->Name() << "." << endl;
		
		// Ask player to choose which weapon they would like to use
		UserSelectWeaponToUse(pPlayer, totalInArray, arrayOfItems);
	}

	// Ask player if they would like to stop combat after each round or not
	sUserInput = "";
	while ((sUserInput != "CONTINUE") && (sUserInput != "STOP"))
	{
		cout << endl << "Would you like to CONTINUE the combat until it is completed or STOP after each round?";
		cin >> sUserInput;
		cout << endl;

		transform(sUserInput.begin(), sUserInput.end(), sUserInput.begin(), toupper);
		
		if ((sUserInput != "CONTINUE") && (sUserInput != "STOP"))
			cout << endl << "The input you entered was not understood. Please try again." << endl;
	}
	
	// Utilize the user input to set the stop flag appropriately
	if (sUserInput == "STOP")
		CUserInterface::StopAfterEachRound = true;
	else if (sUserInput == "CONTINUE")
		CUserInterface::StopAfterEachRound = false;
	else
		cout << "There was an issue with this portion of the code. Please investigate.";

	// Now, determine weapon to be used by the enemy
	int iHighestDamageOutput = 0;
	arrayOfItems = m_cEnemy->RetrieveAllItemsFromInventory(totalInArray);
	for (int i = 0; i < totalInArray; i++)
	{
		// Check to see if the damage output is higher than the one currently selected
		if ((arrayOfItems[i]->DamageDonePerAttack > iHighestDamageOutput))
		{
			// Assign weapon to static class member
			m_sWeaponUsedByEnemy = arrayOfItems[i];

			// Assign the new highest damage output
			iHighestDamageOutput = arrayOfItems[i]->DamageDonePerAttack;
		}
	}

	// OUTPUT TO THE SCREEN THE INFORMATION ABOUT THE FIGHT
	// Output the header for the damage chart
	CUserInterface::ClearScreen();
	string sTextLine;
	// Output the list of weapons being used by each side
	cout << endl;
	sTextLine = "You are attacking with ";
	if (m_sWeaponUsedByPlayer != nullptr)
		sTextLine+= "a(n) " + m_sWeaponUsedByPlayer->Name;
	else
		sTextLine+= "your fists";
	cout.flags(std::ios::left);
	cout.width(39);
	cout << sTextLine;
	sTextLine = "The " + m_cEnemy->Name() + " is attacking with ";
	if (m_sWeaponUsedByEnemy != nullptr)
		sTextLine += "a(n) " + m_sWeaponUsedByEnemy->Name;
	else
		sTextLine += "his/her fists";
	cout.flags(std::ios::right);
	cout.width(39);
	cout << sTextLine;
	cout << endl;
	// Output the weapon damage caused by weapon
	sTextLine = "Damage Dealt: ";
	if (m_sWeaponUsedByPlayer != nullptr)
		sTextLine += to_string(m_sWeaponUsedByPlayer->DamageDonePerAttack);
	else
		sTextLine += to_string(DAMAGE_DONE_BY_FISTS);
	cout.flags(std::ios::left);
	cout.width(39);
	cout << sTextLine;
	sTextLine = "Damage Dealt: ";
	if (m_sWeaponUsedByEnemy != nullptr)
		sTextLine += to_string(m_sWeaponUsedByEnemy->DamageDonePerAttack);
	else
		sTextLine += to_string(DAMAGE_DONE_BY_FISTS);
	cout.flags(std::ios::right);
	cout.width(39);
	cout << sTextLine;
	cout << endl << endl;
	cout.flags(std::ios::left);
	cout.width(39);
	cout << "<Entity> % Chance: Roll"; 
	cout.flags(std::ios::right);
	cout.width(39);
	cout << "Opponent's Health";
	cout << endl;
	cout << std::string(78, '-') << endl;
	// FINISH OUTPUTTING INFORMATION ABOUT THE FIGHT
}

void CCombatInterface::UserSelectWeaponToUse(IPerson* &pPerson, int TotalWeaponsAvailable, SItem** &WeaponOptions)
{
	string sUserInput;

	cout << "Your options for weapons are:" << endl;
	
	cout << setw(25) << left << "Item Name:" << "Damage Done Per Attack" << endl;
	for (int i = 0; i < TotalWeaponsAvailable; i++)
	{
		// If the weapon is capable of doing damage
		if (WeaponOptions[i]->DamageDonePerAttack > 0)
		{
			// Display weapon information to user
			cout << setw(25) << left << WeaponOptions[i]->Name << WeaponOptions[i]->DamageDonePerAttack;
			cout << endl;
			// Display ammunition information if applicable
			if (WeaponOptions[i]->ProjectileWeapon)
			{
				cout << setw(10) << left << " ";
				cout << "Ammo In Weapon: " << WeaponOptions[i]->AmmoInWeapon;
				cout << endl;
			}
		}
	}

	// Prompt the user to select weapon to be used
	cout << endl << "What weapon would you like to use?: ";
	getline(std::cin, sUserInput);

	// convert user input to all upper case
	transform(sUserInput.begin(), sUserInput.end(), sUserInput.begin(), toupper);

	// Get the weapon selected by the user
	string sName;
	for (int i = 0; i < TotalWeaponsAvailable; i++)
	{
		// convert string to all upper case
		sName = WeaponOptions[i]->Name;
		transform(sName.begin(), sName.end(), sName.begin(), toupper);
		// Check if the weapon is the one selected and it has the ability to damage the opponent
		if ((sName == sUserInput) && (WeaponOptions[i]->DamageDonePerAttack > 0))
		{
			// Assign weapon to static class member
			m_sWeaponUsedByPlayer = WeaponOptions[i];
			// Inform the user of the weapon selection
			cout << endl << "You have selected to use " << WeaponOptions[i]->Name << " as your weapon.";
			cout << endl;
		}
	}
	
	// If user-selected option is not found, display error
	if (m_sWeaponUsedByPlayer == nullptr)
	{
		// The option entered by the user was not understood; Recursively go through function
		//	until a valid option is entered by the user.
		SetTextColor(BACKGROUND_RED|BACKGROUND_INTENSITY|FOREGROUND_BLUE|FOREGROUND_RED|FOREGROUND_GREEN);
		cout << endl << "The text you entered was not understood. Please try again." << endl;
		UserSelectWeaponToUse(pPerson, TotalWeaponsAvailable, WeaponOptions);
	}
}

void CCombatInterface::PerformCombatRound()
{
	int iChanceThatEnemyHitsPerson = 0;
	int iChanceThatPersonHitsEnemy = 0;
	int iRandomNumberRolledByPerson = 0;
	int iRandomNumberRolledByEnemy = 0;
	int itotalInArray;
	int iHighestDamageOutput;
	SItem** arrayOfItems = nullptr;;
	bool bEnemyHitsPerson = false;
	bool bPersonHitsEnemy = false;

	// Verify the weapons being used have the ammunition available if appropriate
	if ((m_sWeaponUsedByEnemy != nullptr) && (m_sWeaponUsedByEnemy->ProjectileWeapon)) {
		if (m_sWeaponUsedByEnemy->AmmoInWeapon <= 0)
		{
			// If out of ammunition, remove the item from the enemy's inventory
			m_cEnemy->RemoveFromInventory(m_sWeaponUsedByEnemy);
			m_sWeaponUsedByEnemy = nullptr;
			// delete the item from the game
			//delete m_sWeaponUsedByEnemy;

			// Now, determine the new weapon to be used by the enemy
			iHighestDamageOutput = 0;
			arrayOfItems = m_cEnemy->RetrieveAllWeaponsFromInventory(itotalInArray);
			for (int i = 0; i < itotalInArray; i++)
			{
				// Check to see if the damage output is higher than the one currently selected
				if ((arrayOfItems[i]->DamageDonePerAttack > iHighestDamageOutput))
				{
					// Assign weapon to static class member
					m_sWeaponUsedByEnemy = arrayOfItems[i];

					// Assign the new highest damage output
					iHighestDamageOutput = arrayOfItems[i]->DamageDonePerAttack;
				}
			}
		}
	}
	if ((m_sWeaponUsedByPlayer != nullptr) && (m_sWeaponUsedByPlayer->ProjectileWeapon)) {
		if (m_sWeaponUsedByPlayer->AmmoInWeapon <= 0)
		{
			// If out of ammunition, remove the item from the player's inventory
			m_cPerson->RemoveFromInventory(m_sWeaponUsedByPlayer);
			m_sWeaponUsedByPlayer = nullptr;
			// delete the item from the game
			//delete m_sWeaponUsedByPlayer;

			// Now, determine the new weapon to be used by the player
			arrayOfItems = m_cPerson->RetrieveAllWeaponsFromInventory(itotalInArray);
			cout << endl << "Your weapon you were using has run out of ammunition. Please select another weapon." << endl;
			UserSelectWeaponToUse(m_cPerson, itotalInArray, arrayOfItems);
		}
	}

	// Perform a round of combat

	// Determine the chance that the enemy will hit the person
	if ((m_sWeaponUsedByEnemy != nullptr) && (m_sWeaponUsedByEnemy->ProjectileWeapon)) {
		// Go ahead and subtract the shot from the weapon
		m_sWeaponUsedByEnemy->AmmoInWeapon--;
		iChanceThatEnemyHitsPerson = PERCENT_CHANCE_FOR_ENEMY_TO_HIT_WITH_RANGED_WEAPON;
	} else {
		iChanceThatEnemyHitsPerson = PERCENT_CHANCE_FOR_ENEMY_TO_HIT_WITH_MELEE_WEAPON;
	}
	// Determine the chance that the person will hit the enemy
	if ((m_sWeaponUsedByPlayer != nullptr) && (m_sWeaponUsedByPlayer->ProjectileWeapon))
	{
		// Go ahead and subtract the shot from the weapon
		m_sWeaponUsedByPlayer->AmmoInWeapon--;
		iChanceThatPersonHitsEnemy = PERCENT_CHANCE_FOR_PERSON_TO_HIT_WITH_RANGED_WEAPON;
	} else {
		iChanceThatPersonHitsEnemy = PERCENT_CHANCE_FOR_PERSON_TO_HIT_WITH_MELEE_WEAPON;
	}

	// Use random number to determine if enemy hits person
	iRandomNumberRolledByEnemy = CRandom::GetRandomNumber(100);
	if (iRandomNumberRolledByEnemy <= iChanceThatEnemyHitsPerson)
	{
		bEnemyHitsPerson = true;
	}
	// Use random number to determine if person hits enemy
	iRandomNumberRolledByPerson = CRandom::GetRandomNumber(100);
	if (iRandomNumberRolledByPerson <= iChanceThatPersonHitsEnemy)
	{
		bPersonHitsEnemy = true;
	}

	if (bEnemyHitsPerson)	// Enemy has hit the Person
	{
		// Apply damage to enemy health
		if (m_sWeaponUsedByEnemy != nullptr) {
			m_cPerson->SetHealth(m_cPerson->Health() - m_sWeaponUsedByEnemy->DamageDonePerAttack);
		} else {
			m_cPerson->SetHealth(m_cPerson->Health() - DAMAGE_DONE_BY_FISTS);
		}
		if (m_cPerson->Health() < 1)		// If the person has been killed
		{
			m_bCombatComplete = true;
			CUserInterface::UserDied = true;
		}
	}

	if (bPersonHitsEnemy)	// Person has hit the Enemy
	{
		if (m_sWeaponUsedByPlayer != nullptr) {
			m_cEnemy->SetHealth(m_cEnemy->Health() - m_sWeaponUsedByPlayer->DamageDonePerAttack);
		} else {
			m_cEnemy->SetHealth(m_cEnemy->Health() - DAMAGE_DONE_BY_FISTS);
		}
		if (m_cEnemy->Health() < 1)		// If the enemy has been killed
		{
			m_bCombatComplete = true;	
		}
	}

	// Output the lines of information
	// Output enemy's line
	if (bEnemyHitsPerson)
		SetTextColor(FOREGROUND_RED|FOREGROUND_INTENSITY);
	else
		SetTextColor(FOREGROUND_GREEN|FOREGROUND_INTENSITY);
	string sTextLine = "<" + m_cEnemy->Name() + "> "; 
	sTextLine += to_string(iChanceThatEnemyHitsPerson) + ": " + to_string(iRandomNumberRolledByEnemy);
	cout.flags(std::ios::left);
	cout.width(39);
	cout << sTextLine;
	sTextLine = "<" + m_cPerson->Name() + ">" + to_string(m_cPerson->Health());
	cout.flags(std::ios::right);
	cout.width(39);
	cout << sTextLine;
	cout << endl;
	// Output person's line
	if (bPersonHitsEnemy)
		SetTextColor(FOREGROUND_RED|FOREGROUND_INTENSITY);
	else
		SetTextColor(FOREGROUND_GREEN|FOREGROUND_INTENSITY);
	sTextLine = "<" + m_cPerson->Name() + "> "; 
	sTextLine += to_string(iChanceThatPersonHitsEnemy) + ": " + to_string(iRandomNumberRolledByPerson);
	cout.flags(std::ios::left);
	cout.width(39);
	cout << sTextLine;
	sTextLine = "<" + m_cEnemy->Name() + ">" + to_string(m_cEnemy->Health());
	cout.flags(std::ios::right);
	cout.width(39);
	cout << sTextLine;
	cout << endl;
}

bool CCombatInterface::CombatCompleted()
{
	return m_bCombatComplete;
}
